using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using System.Text;
using System.Xml.Serialization;
using System.Runtime.Serialization;

namespace TicTacToe
{
    [DataContract]
    public enum TicTacToePlayer { [EnumMember(Value = "None")]None, [EnumMember(Value = "PlayerO")] PlayerO, [EnumMember(Value = "PlayerX")] PlayerX }
    public enum BoardSize { Three_x_Three = 3, Six_x_Six = 6 }
    public enum Screen { Main, Option }
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        Screen screen;
        BoardSize boardSize = BoardSize.Six_x_Six;//for test
        int size;
        int numToWin = 3;//khong the = 1

        int numDraw;
        int numXWin;
        int numOWin;

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Texture2D gridTexture;
        Rectangle gridRectangle;

        Texture2D resetButton;
        Rectangle resetButtonPosition;

        bool showingSaveButton;
        Texture2D saveButton;
        Rectangle saveAndLoadButtonPosition;
        Texture2D loadButton;

        Texture2D optionButton;
        Rectangle optionButtonPosition;

        Texture2D undoButton;
        Rectangle undoButtonPosition;

        Texture2D oPiece;
        Texture2D xPiece;

        Texture2D oWinner;
        Texture2D xWinner;
        Texture2D noWinner;

        Texture2D oTurn;
        Texture2D xTurn;

        bool winnable;
        TicTacToePlayer winner;
        TicTacToePlayer current;
        TicTacToePlayer[,] grid;

        bool touching;

        int backBufferedWidth;
        int backBufferedHeight;
        int squareEdge;

        public Game1()
        {
            numXWin = 0;
            numOWin = 0;
            numDraw = 0;

            showingSaveButton = true;
            showingSaveButton = !GameSaver.CanLoad();
            screen = Screen.Main;

            backBufferedWidth = 480;
            backBufferedHeight = 800;

            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            graphics.PreferredBackBufferWidth = backBufferedWidth;
            graphics.PreferredBackBufferHeight = backBufferedHeight;

            // Frame rate is 30 fps by default for Windows Phone.
            TargetElapsedTime = TimeSpan.FromTicks(333333);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            Reset();
            base.Initialize();
        }

        private void Reset()
        {
            current = TicTacToePlayer.PlayerO;
            applyOption();

            size = (int)boardSize;
            squareEdge = backBufferedWidth / size - 5;

            winnable = true;
            winner = TicTacToePlayer.None;

            grid = new TicTacToePlayer[size, size];
            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < size; j++)
                {
                    grid[i, j] = TicTacToePlayer.None;
                }
            }
            applyOption();
        }

        private void applyOption()
        {
            // TODO: try catch
            boardSize = (BoardSize)Enum.Parse(typeof(BoardSize), Optioner.Get(Optioner.BOARD_SIZE).ToString(), false);
            current = (TicTacToePlayer)Enum.Parse(typeof(TicTacToePlayer), Optioner.Get(Optioner.PLAY_FIRST).ToString(), false);
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            gridRectangle = new Rectangle(0, 0, spriteBatch.GraphicsDevice.Viewport.Width, spriteBatch.GraphicsDevice.Viewport.Height);
            resetButton = Content.Load<Texture2D>("TicTacToe_Reset");
            resetButtonPosition = new Rectangle(spriteBatch.GraphicsDevice.Viewport.Width / 2 - (resetButton.Width / 2), spriteBatch.GraphicsDevice.Viewport.Height - 95, resetButton.Width, resetButton.Height);
            saveButton = Content.Load<Texture2D>("SaveButton");
            loadButton = Content.Load<Texture2D>("LoadButton");
            saveAndLoadButtonPosition = new Rectangle(resetButtonPosition.X - 10, resetButtonPosition.Y - resetButtonPosition.Height, saveButton.Width, saveButton.Height);
            optionButton = Content.Load<Texture2D>("OptionButton");
            optionButtonPosition = new Rectangle(saveAndLoadButtonPosition.X + saveButton.Width, saveAndLoadButtonPosition.Y, optionButton.Width, optionButton.Height);
            undoButton = Content.Load<Texture2D>("UndoButton");
            undoButtonPosition = new Rectangle(optionButtonPosition.X + saveButton.Width, saveAndLoadButtonPosition.Y, undoButton.Width, undoButton.Height);

            oWinner = Content.Load<Texture2D>("TicTacToe_O_Winner");
            xWinner = Content.Load<Texture2D>("TicTacToe_X_Winner");
            noWinner = Content.Load<Texture2D>("TicTacToe_Draw");

            oTurn = Content.Load<Texture2D>("TicTacToe_O_Turn");
            xTurn = Content.Load<Texture2D>("TicTacToe_X_Turn");

            if (boardSize == BoardSize.Six_x_Six)
            {
                gridTexture = Content.Load<Texture2D>("TicTacToe_Grid_6x6");
                oPiece = Content.Load<Texture2D>("TicTacToe_O_6x6");
                xPiece = Content.Load<Texture2D>("TicTacToe_X_6x6");
            }
            else if (boardSize == BoardSize.Three_x_Three)
            {
                gridTexture = Content.Load<Texture2D>("TicTacToe_Grid_3x3");
                oPiece = Content.Load<Texture2D>("TicTacToe_O_3x3");
                xPiece = Content.Load<Texture2D>("TicTacToe_X_3x3");
            }

            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            Font1 = Content.Load<SpriteFont>("Segoe UI Mono");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        // Run periodcally
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            {
                this.Exit();
            }

            TouchCollection touches = TouchPanel.GetState();

            if (!touching && touches.Count > 0)
            {
                touching = true;
                TouchLocation touch = touches.First();

                if (screen == Screen.Main)
                {
                    HandleBoardTouch(touch);
                    HandleResetTouch(touch);
                    HandleMenuTouch(touch);
                }
                else if (screen == Screen.Option)
                {

                }
            }
            else if (touches.Count == 0)
            {
                touching = false;
            }

            base.Update(gameTime);
        }

        private void HandleResetTouch(TouchLocation touch)
        {
            if ((winner != TicTacToePlayer.None || !winnable)
                && resetButtonPosition.Contains((int)touch.Position.X, (int)touch.Position.Y))
            {
                Reset();
            }
        }

        private void HandleMenuTouch(TouchLocation touch)
        {
            if (saveAndLoadButtonPosition.Contains((int)touch.Position.X, (int)touch.Position.Y))
            {                
                if (showingSaveButton)
                {
                    //Save clicked
                    GameSaver.Save(grid, current);
                }
                else
                {
                    //Load clicked
                    grid = GameSaver.LoadGrid();
                    current = GameSaver.LoadCurrent();
                }

                showingSaveButton = !showingSaveButton;
            }

            if (optionButtonPosition.Contains((int)touch.Position.X, (int)touch.Position.Y))
            {
                screen = Screen.Option;
            }

            if (undoButtonPosition.Contains((int)touch.Position.X, (int)touch.Position.Y))
            {
            }
        }

        private void HandleBoardTouch(TouchLocation touch)
        {
            if (winner == TicTacToePlayer.None)
            {
                TicTacToePlayer prev = current;
                for (int i = 0; i < size; i++)
                {
                    for (int j = 0; j < size; j++)
                    {
                        Rectangle box = GetGridSpace(i, j, squareEdge, squareEdge);
                        if (grid[i, j] == TicTacToePlayer.None && box.Contains((int)touch.Position.X, (int)touch.Position.Y))
                        {
                            grid[i, j] = current;
                            current = current == TicTacToePlayer.PlayerO ? TicTacToePlayer.PlayerX : TicTacToePlayer.PlayerO;
                        }
                    }
                }

                CheckForWin(prev);
                CheckForWinnable();
            }

            if ((!winnable || winner != TicTacToePlayer.None) && !resetButtonPosition.Contains((int)touch.Position.X, (int)touch.Position.Y))
            {
                handleStatistics();
            }
        }

        private void handleStatistics()
        {
            if (winner == TicTacToePlayer.PlayerO)
            {
                numOWin++;
            }

            if (winner == TicTacToePlayer.PlayerX)
            {
                numXWin++;
            }

            if (!winnable)
            {
                numDraw++;
            }
        }

        private void CheckForWin(TicTacToePlayer player)
        {
            checkWinOnColumn(player);
            checkWinOnRow(player);
            checkWinOnLeftToRight(player);
            checkWinOnRightToLeft(player);
        }

        private void checkWinOnLeftToRight(TicTacToePlayer player)
        {
            for (int j = size - numToWin; j >= 0; j--)
            {
                int dem = 1;
                int demDX = 1;
                bool startCount = false;
                bool startCountDX = false;
                TicTacToePlayer c = TicTacToePlayer.None;
                TicTacToePlayer cDX = TicTacToePlayer.None;
                for (int i = 0, k = j; i < size - j; i++, k++)
                {
                    if (grid[i, k] == TicTacToePlayer.None)
                    {
                        startCount = false;
                    }
                    else if (!startCount)
                    {
                        startCount = true;
                        c = grid[i, k];
                        dem = 1;
                        continue;
                    }

                    if (startCount)
                    {
                        if (grid[i, k] == c)
                        {
                            dem++;
                        }
                        else
                        {
                            dem = 1;
                            c = grid[i, k];
                        }
                    }


                    // phan doi xung
                    if (grid[k, i] == TicTacToePlayer.None)
                    {
                        startCountDX = false;
                    }
                    else if (!startCountDX)
                    {
                        startCountDX = true;
                        cDX = grid[k, i];
                        demDX = 1;
                        continue;
                    }

                    if (startCountDX)
                    {
                        if (grid[k, i] == cDX)
                        {
                            demDX++;
                        }
                        else
                        {
                            demDX = 1;
                            cDX = grid[k, i];
                        }
                    }

                    if (dem == numToWin || demDX == numToWin)
                    {
                        winner = player;
                        return;
                    }
                }
            }
        }

        private void checkWinOnRightToLeft(TicTacToePlayer player)
        {
            for (int j = size - 1; j >= numToWin - 1; j--)
            {
                int dem = 1;
                int demDX = 1;
                bool startCount = false;
                bool startCountDX = false;
                TicTacToePlayer c = TicTacToePlayer.None;
                TicTacToePlayer cDX = TicTacToePlayer.None;
                for (int i = 0, k = j; i <= j; i++, k--)
                {
                    if (grid[i, k] == TicTacToePlayer.None)
                    {
                        startCount = false;
                    }
                    else if (!startCount)
                    {
                        startCount = true;
                        c = grid[i, k];
                        dem = 1;
                        continue;
                    }

                    if (startCount)
                    {
                        if (grid[i, k] == c)
                        {
                            dem++;
                        }
                        else
                        {
                            dem = 1;
                            c = grid[i, k];
                        }
                    }


                    // phan doi xung
                    int iDX = size - k - 1;
                    int jDX = size - i - 1;
                    if (grid[iDX, jDX] == TicTacToePlayer.None)
                    {
                        startCountDX = false;
                    }
                    else if (!startCountDX)
                    {
                        startCountDX = true;
                        cDX = grid[iDX, jDX];
                        demDX = 1;
                        continue;
                    }

                    if (startCountDX)
                    {
                        if (grid[iDX, jDX] == cDX)
                        {
                            demDX++;
                        }
                        else
                        {
                            demDX = 1;
                            cDX = grid[iDX, jDX];
                        }
                    }

                    if (dem == numToWin || demDX == numToWin)
                    {
                        winner = player;
                        return;
                    }
                }
            }
        }

        private void checkWinOnColumn(TicTacToePlayer player)
        {
            for (int i = 0; i < size; i++)
            {
                TicTacToePlayer c = TicTacToePlayer.None;
                int j;
                for (j = 0; j <= size - numToWin; j++)
                {
                    if (grid[i, j] != TicTacToePlayer.None)
                    {
                        c = grid[i, j];
                        j++;
                        break;
                    }
                }

                int dem = 1;
                for (; j < size; j++)
                {
                    if (grid[i, j] == TicTacToePlayer.None)
                    {
                        for (; j <= size - numToWin; j++)
                        {
                            if (grid[i, j] != TicTacToePlayer.None)
                            {
                                c = grid[i, j];
                                dem = 1;
                                j++;
                                break;
                            }
                        }

                        if (j == size - numToWin + 1)
                        {
                            break;
                        }

                        continue;
                    }

                    if (grid[i, j] == c)
                    {
                        dem++;

                        if (dem == numToWin)
                        {
                            winner = player;
                            return;
                        }
                    }
                    else
                    {
                        dem = 1;
                        c = grid[i, j];
                    }
                }
            }
        }

        private void checkWinOnRow(TicTacToePlayer player)
        {
            for (int j = 0; j < size; j++)
            {
                TicTacToePlayer c = TicTacToePlayer.None;
                int i;
                for (i = 0; i <= size - numToWin; i++)
                {
                    if (grid[i, j] != TicTacToePlayer.None)
                    {
                        c = grid[i, j];
                        i++;
                        break;
                    }
                }

                int dem = 1;
                for (; i < size; i++)
                {
                    if (grid[i, j] == TicTacToePlayer.None)
                    {
                        for (; i <= size - numToWin; i++)
                        {
                            if (grid[i, j] != TicTacToePlayer.None)
                            {
                                c = grid[i, j];
                                dem = 1;
                                i++;
                                break;
                            }
                        }

                        if (i == size - numToWin + 1)
                        {
                            break;
                        }

                        continue;
                    }

                    if (grid[i, j] == c)
                    {
                        dem++;

                        if (dem == numToWin)
                        {
                            winner = player;
                            return;
                        }
                    }
                    else
                    {
                        dem = 1;
                        c = grid[i, j];
                    }
                }
            }
        }

        private void CheckForWinnable()
        {
            if (winner == TicTacToePlayer.None)
            {
                Func<TicTacToePlayer, bool> checkNone = b => b == TicTacToePlayer.None;
                for (int i = 0; i < size; i++)
                {
                    for (int j = 0; j < size; j++)
                    {
                        if (grid[i, j] == TicTacToePlayer.None)
                        {
                            winnable = true;
                            // TODO: co the cai tien
                            return;
                        }
                    }
                }

                winnable = false;
            }
        }

        SpriteFont Font1;
        Vector2 FontPos;
        // Run periodcally
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin();

            if (screen == Screen.Main)
            {
                DrawGrid();
                DrawPieces();
                DrawStatus();
                DrawResetButton();
                DrawMenu();
                DrawStatistic();
            }
            else if (screen == Screen.Option)
            {

            }

            spriteBatch.End();

            base.Draw(gameTime);
        }

        private void DrawStatistic()
        {
            string textNumXWin = WrapText(Font1, "X Win: " + numXWin, 160);
            // Find the center of the string
            Vector2 FontOrigin = Font1.MeasureString(textNumXWin) / 2;
            int startXPos = 80;
            int yPos = 125;
            FontPos = new Vector2(startXPos, yPos);
            spriteBatch.DrawString(Font1, textNumXWin, FontPos, Color.LightGreen,
                0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);

            string textNumDraw = WrapText(Font1, "Draw: " + numDraw, 160);
            FontOrigin = Font1.MeasureString(textNumDraw) / 2;
            startXPos += 160;
            FontPos = new Vector2(startXPos, yPos);
            spriteBatch.DrawString(Font1, textNumDraw, FontPos, Color.LightGreen,
                0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);

            string textNumOWin = WrapText(Font1, "O Win: " + numOWin, 160);
            FontOrigin = Font1.MeasureString(textNumOWin) / 2;
            startXPos += 160;
            FontPos = new Vector2(startXPos, yPos);
            spriteBatch.DrawString(Font1, textNumOWin, FontPos, Color.LightGreen,
                0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
        }

        private void DrawMenu()
        {
            if (showingSaveButton)
            {
                spriteBatch.Draw(saveButton, saveAndLoadButtonPosition, Color.White);
            }
            else
            {
                spriteBatch.Draw(loadButton, saveAndLoadButtonPosition, Color.White);
            }

            spriteBatch.Draw(optionButton, optionButtonPosition, Color.White);
            spriteBatch.Draw(undoButton, undoButtonPosition, Color.White);
        }

        private void DrawGrid()
        {
            spriteBatch.Draw(gridTexture, gridRectangle, Color.Red);
        }

        private void DrawPieces()
        {
            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < size; j++)
                {
                    if (grid[i, j] != TicTacToePlayer.None)
                    {
                        Texture2D texture = grid[i, j] == TicTacToePlayer.PlayerO ? oPiece : xPiece;
                        Rectangle position = GetGridSpace(i, j, texture.Width, texture.Height);
                        spriteBatch.Draw(texture, position, Color.White);
                    }
                }
            }
        }

        private void DrawStatus()
        {
            Texture2D texture;
            if (winner != TicTacToePlayer.None)
            {
                texture = winner == TicTacToePlayer.PlayerO ? oWinner : xWinner;
            }
            else if (!winnable)
            {
                texture = noWinner;
            }
            else
            {
                texture = current == TicTacToePlayer.PlayerO ? oTurn : xTurn;
            }

            Rectangle position = new Rectangle(spriteBatch.GraphicsDevice.Viewport.Width / 2 - (texture.Width / 2), 15, texture.Width, texture.Height);
            spriteBatch.Draw(texture, position, Color.White);
        }

        private void DrawResetButton()
        {
            if (winner != TicTacToePlayer.None || !winnable)
            {
                spriteBatch.Draw(resetButton, resetButtonPosition, Color.White);
            }
        }

        private Rectangle GetGridSpace(int column, int row, int width, int height)
        {
            int centerX = spriteBatch.GraphicsDevice.Viewport.Width / 2;
            int centerY = spriteBatch.GraphicsDevice.Viewport.Height / 2;

            int x = centerX + (column - (this.size / 2)) * squareEdge + (squareEdge - squareEdge * (this.size % 2)) / 2 - (width / 2);
            int y = centerY + (row - (this.size / 2)) * squareEdge + (squareEdge - squareEdge * (this.size % 2)) / 2 - (height / 2);

            return new Rectangle(x, y, width, height);
        }

        public string WrapText(SpriteFont spriteFont, string text, float maxLineWidth)
        {
            string[] words = text.Split(' ');

            StringBuilder sb = new StringBuilder();

            float lineWidth = 0f;

            float spaceWidth = spriteFont.MeasureString(" ").X;

            foreach (string word in words)
            {
                Vector2 size = spriteFont.MeasureString(word);

                if (lineWidth + size.X < maxLineWidth)
                {
                    sb.Append(word + " ");
                    lineWidth += size.X + spaceWidth;
                }
                else
                {
                    sb.Append("\n" + word + " ");
                    lineWidth = size.X + spaceWidth;
                }
            }

            return sb.ToString();
        }
    }
}
